﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Graphics;
using System.Windows.Input;
using System.Windows.Threading;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Resources;
using Keyboard = Microsoft.Xna.Framework.Input.Keyboard;

namespace Microsoft.Xna.Framework
{
    /// <summary>
    /// 	Provides basic graphics device initialization, game logic, and rendering code.
    /// </summary>
    public class Game : UserControl, IDisposable
    {
        #region Fields & Properties

        private GraphicsDeviceManager _graphicsDeviceManager;

        /// <summary>
        /// 	Gets the current GraphicsDevice.
        /// </summary>
        public GraphicsDevice GraphicsDevice
        {
            get
            {
                var graphicsDeviceManager = _graphicsDeviceManager;
                if (graphicsDeviceManager == null)
                {
                    graphicsDeviceManager = GraphicsDeviceManager.Current;
                    if (graphicsDeviceManager == null)
                    {
                        throw new InvalidOperationException(Resx.NoGraphicsDeviceService);
                    }
                }
                return graphicsDeviceManager.GraphicsDevice;
            }
        }

        private readonly GameServiceContainer _gameServices = new GameServiceContainer();

        /// <summary>
        /// 	Gets the GameServiceContainer holding all the service providers attached to the Game.
        /// </summary>
        public GameServiceContainer Services
        {
            get
            {
                return _gameServices;
            }
        }

        private ContentManager _content;

        /// <summary>
        /// 	Gets or sets the current ContentManager.
        /// </summary>
        [CLSCompliant(false)]
        public new ContentManager Content
        {
            get
            {
                return _content;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }
                _content = value;
            }
        }

        #endregion

        #region Collection Fields & Properties

        private readonly List<IUpdateable> _updateableComponents = new List<IUpdateable>();
        private readonly List<IUpdateable> _currentlyUpdatingComponents = new List<IUpdateable>();

        private readonly List<IDrawable> _drawableComponents = new List<IDrawable>();
        private readonly List<IDrawable> _currentlyDrawingComponents = new List<IDrawable>();

        private readonly List<IGameComponent> _unInitializedGameComponents = new List<IGameComponent>();
        private readonly GameComponentCollection _gameComponents = new GameComponentCollection();

        /// <summary>
        /// 	Gets the collection of GameComponents owned by the game.
        /// </summary>
        public GameComponentCollection Components
        {
            get
            {
                return _gameComponents;
            }
        }

        #endregion

        #region Status Fields & Properties

        private bool _isActive;

        /// <summary>
        /// 	Indicates whether the game is currently the active application.
        /// </summary>
        public bool IsActive
        {
            get
            {
                return _isActive;
            }
        }

        private bool _isMouseVisible = true;

        /// <summary>
        /// 	Gets or sets a value indicating whether the mouse cursor should be visible.
        /// </summary>
        public bool IsMouseVisible
        {
            get
            {
                return _isMouseVisible;
            }
            set
            {
                _isMouseVisible = value;
                if (Window != null)
                {
                    Window.Cursor = _isMouseVisible ? Cursors.Arrow : Cursors.None;
                }
            }
        }

        #endregion

        #region Time Fields & Properties

        private readonly TimeSpan _maximumElapsedTime = TimeSpan.FromMilliseconds(500.0);
        private TimeSpan _totalGameTime = TimeSpan.Zero;
        private TimeSpan _accumulatedElapsedGameTime = TimeSpan.Zero;
        private TimeSpan _lastFrameElapsedGameTime = TimeSpan.Zero;
        private TimeSpan _inactiveSleepTime = TimeSpan.FromMilliseconds(20.0);
        private TimeSpan _targetElapsedTime = TimeSpan.FromTicks(333333L);

        /// <summary>
        /// 	Gets or sets the target time between calls to Update when IsFixedTimeStep is true.
        /// </summary>
        public TimeSpan TargetElapsedTime
        {
            get
            {
                return _targetElapsedTime;
            }
            set
            {
                if (value <= TimeSpan.Zero)
                {
                    throw new ArgumentOutOfRangeException("value", Resx.TargetElaspedCannotBeZero);
                }
                _targetElapsedTime = value;
            }
        }

        /// <summary>
        /// 	Gets or sets the time to sleep when the game is inactive.
        /// </summary>
        public TimeSpan InactiveSleepTime
        {
            get
            {
                return _inactiveSleepTime;
            }
            set
            {
                if (value < TimeSpan.Zero)
                {
                    throw new ArgumentOutOfRangeException("value", Resx.InactiveSleepTimeCannotBeZero);
                }
                _inactiveSleepTime = value;
            }
        }

        private readonly GameTime _gameTime = new GameTime();
        private readonly GameClock _clock = new GameClock();

        private bool _isFixedTimeStep = true;

        /// <summary>
        /// 	Gets or sets a value indicating whether to use fixed time steps.
        /// </summary>
        public bool IsFixedTimeStep
        {
            get
            {
                return _isFixedTimeStep;
            }
            set
            {
                _isFixedTimeStep = value;
            }
        }

        private bool _forceElapsedTimeToZero;

        #endregion

        #region Events

        /// <summary>
        /// 	Raised when the game gains focus.
        /// </summary>
        public event EventHandler<EventArgs> Activated;

        /// <summary>
        /// 	Raises the Activated event. Override this method to add code to handle when the game gains focus.
        /// </summary>
        protected virtual void OnActivated()
        {
            var onActivated = Activated;
            if (onActivated != null)
            {
                onActivated(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// 	Raised when the game loses focus.
        /// </summary>
        public event EventHandler<EventArgs> Deactivated;

        /// <summary>
        /// 	Raises the Deactivated event. Override this method to add code to handle when the game loses focus.
        /// </summary>
        protected virtual void OnDeactivated()
        {
            var onDeactivated = Deactivated;
            if (onDeactivated != null)
            {
                onDeactivated(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// 	Raised when the game is exiting.
        /// </summary>
        public event EventHandler<EventArgs> Exiting;

        /// <summary>
        /// 	Raises an Exiting event. Override this method to add code to handle when the game is exiting.
        /// </summary>
        protected virtual void OnExiting()
        {
            var onExiting = Exiting;
            if (onExiting != null)
            {
                onExiting(this, EventArgs.Empty);
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// 	Initializes a new instance of this class, which provides basic graphics device initialization, game logic, rendering code, and a game loop.
        /// </summary>
        public Game()
        {
            EnsureHost();

            _gameComponents.ComponentAdded += HandleGameComponentsComponentAdded;
            _gameComponents.ComponentRemoved += HandleGameComponentsComponentRemoved;

            _content = new ContentManager(_gameServices, "Content");
        }

        /// <summary>
        /// 	Allows a Game to attempt to free resources and perform other cleanup operations before garbage collection reclaims the Game.
        /// </summary>
        ~Game()
        {
            Dispose(false);
        }

        #endregion

        #region Implementation of IDisposable

        #region Dispose

        /// <summary>
        /// 	Releases all resources used by the Game class.
        /// </summary>
        /// <param name="disposing"> true to release both managed and unmanaged resources; false to release only unmanaged resources. </param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                lock (this)
                {
                    var copiedGameComponents = _gameComponents.ToArray();
                    foreach (var disposable in copiedGameComponents.OfType<IDisposable>())
                    {
                        disposable.Dispose();
                    }
                    if (_graphicsDeviceManager != null)
                    {
                        _graphicsDeviceManager.RenderModeChanged -= HandleGraphicsDeviceManagerRenderModeChanged;
                        _content.Unload();
                        UnloadContent();
                    }
                    OnDisposed();
                }
            }
        }

        /// <summary>
        /// 	Raised when the game is being disposed.
        /// </summary>
        public event EventHandler<EventArgs> Disposed;

        /// <summary>
        /// 	Called when the Dispose method is invoked. Raises the Disposed event.
        /// </summary>
        private void OnDisposed()
        {
            var onDisposed = Disposed;
            if (onDisposed != null)
            {
                onDisposed(this, EventArgs.Empty);
            }
        }

        #endregion

        /// <summary>
        /// 	Immediately releases the unmanaged resources used by this object.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

        #region Host

        private readonly DrawingSurface _drawingWindow = new DrawingSurface();

        /// <summary>
        /// 	Gets the underlying drawing surface window.
        /// </summary>
        public DrawingSurface Window
        {
            get
            {
                return _drawingWindow;
            }
        }

        private readonly DispatcherTimer _hostTimer = new DispatcherTimer();

        #region Ensure Host

        private void EnsureHost()
        {
            _drawingWindow.Loaded += HandleDrawingWindowLoaded;
            _drawingWindow.Unloaded += HandleDrawingWindowUnloaded;
            _drawingWindow.Draw += HandleDrawingWindowDraw;
            base.Content = _drawingWindow;

            _hostTimer.Tick += HandleHostTimerTick;
        }

        #endregion

        #region Event Handlers

        private void HandleDrawingWindowLoaded(object sender, RoutedEventArgs e)
        {
            var rootControl = Application.Current.RootVisual as UserControl;
            if (rootControl == null)
            {
                Mouse.RootControl = this;
                Keyboard.RootControl = this;
            }
            else
            {
                Mouse.RootControl = rootControl;
                Keyboard.RootControl = rootControl;
            }
            if (!_isActive)
            {
                _isActive = true;
                OnActivated();
            }
        }

        private void HandleDrawingWindowUnloaded(object sender, RoutedEventArgs e)
        {
            if (_isActive)
            {
                _isActive = false;
                OnDeactivated();
            }
        }

        private void HandleHostTimerTick(object sender, EventArgs e)
        {
            Tick();
        }

        private void HandleDrawingWindowDraw(object sender, DrawEventArgs e)
        {
            if (_startDrawLoop)
            {
                DrawFrame();
            }
        }

        #endregion

        #endregion

        #region Initialize

        /// <summary>
        /// 	Called after the Game and GraphicsDevice are created, but before LoadContent.
        /// </summary>
        protected virtual void Initialize()
        {
            _graphicsDeviceManager = GraphicsDeviceManager.Current;
            if (_graphicsDeviceManager != null)
            {
                if (_graphicsDeviceManager.RenderMode != RenderMode.Hardware)
                {
                    switch (GraphicsDeviceManager.Current.RenderModeReason)
                    {
                        case RenderModeReason.GPUAccelerationDisabled:
                            throw new InvalidOperationException(Resx.NoGPUAcceleration);
                        case RenderModeReason.SecurityBlocked:
                            throw new InvalidOperationException(Resx.HardwareAccelerationBlockedBySecurityReason);
                        case RenderModeReason.Not3DCapable:
                            throw new InvalidOperationException(Resx.HardwareAccelerationNotAvailable);
                        case RenderModeReason.TemporarilyUnavailable:
                            throw new InvalidOperationException(Resx.HardwareAccelerationNotAvailable);
                    }
                }
                _graphicsDeviceManager.RenderModeChanged += HandleGraphicsDeviceManagerRenderModeChanged;
            }
            while (_unInitializedGameComponents.Count != 0)
            {
                _unInitializedGameComponents[0].Initialize();
                _unInitializedGameComponents.RemoveAt(0);
            }
            if (_graphicsDeviceManager != null && _graphicsDeviceManager.GraphicsDevice != null)
            {
                LoadContent();
            }
        }

        #endregion

        #region Load & Unload Content

        /// <summary>
        /// 	Called when graphics resources need to be loaded. Override this method to load any component-specific graphics resources.
        /// </summary>
        protected virtual void LoadContent()
        {
        }

        /// <summary>
        /// 	Called when graphics resources need to be unloaded. Override this method to unload any game-specific graphics resources.
        /// </summary>
        protected virtual void UnloadContent()
        {
        }

        #endregion

        #region Update

        private bool _doneFirstUpdate;
        private int _updatesSinceRunningSlowly1 = int.MaxValue;
        private int _updatesSinceRunningSlowly2 = int.MaxValue;

        /// <summary>
        /// 	Reference page contains links to related conceptual articles.
        /// </summary>
        /// <param name="gameTime"> Time passed since the last call to Update. </param>
        protected virtual void Update(GameTime gameTime)
        {
            foreach (var updateable in _updateableComponents)
            {
                _currentlyUpdatingComponents.Add(updateable);
            }
            foreach (var updateable in _currentlyUpdatingComponents.Where(updateable => updateable.Enabled))
            {
                updateable.Update(gameTime);
            }
            _currentlyUpdatingComponents.Clear();
            _doneFirstUpdate = true;
        }

        #endregion

        #region Draw

        private bool _startDrawLoop;
        private bool _drawRunningSlowly;
        private bool _suppressDraw;

        /// <summary>
        /// </summary>
        /// <param name="gameTime"> Time passed since the last call to Draw. </param>
        protected virtual void Draw(GameTime gameTime)
        {
            foreach (var drawable in _drawableComponents)
            {
                _currentlyDrawingComponents.Add(drawable);
            }
            foreach (var drawable in _currentlyDrawingComponents.Where(drawable => drawable.Visible))
            {
                drawable.Draw(gameTime);
            }
            _currentlyDrawingComponents.Clear();
        }

        /// <summary>
        /// 	Starts the drawing of a frame. This method is followed by calls to Draw and EndDraw.
        /// </summary>
        protected virtual bool BeginDraw()
        {
            return true;
        }

        /// <summary>
        /// 	Ends the drawing of a frame. This method is preceeded by calls to Draw and BeginDraw.
        /// </summary>
        protected virtual void EndDraw()
        {
        }

        private void DrawFrame()
        {
            try
            {
                if (!_exitRequested && _doneFirstUpdate && BeginDraw())
                {
                    _gameTime.TotalGameTime = _totalGameTime;
                    _gameTime.ElapsedGameTime = _lastFrameElapsedGameTime;
                    _gameTime.IsRunningSlowly = _drawRunningSlowly;
                    Draw(_gameTime);
                    EndDraw();
                }
            }
            finally
            {
                _lastFrameElapsedGameTime = TimeSpan.Zero;
            }
        }

        /// <summary>
        /// 	Prevents calls to Draw until the next Update.
        /// </summary>
        public void SuppressDraw()
        {
            _suppressDraw = true;
        }

        #endregion

        #region Tick

        /// <summary>
        /// 	Updates the game's clock and calls Update and Draw.
        /// </summary>
        public void Tick()
        {
            if (!_exitRequested)
            {
                if (!_isActive)
                {
                    Thread.Sleep((int) _inactiveSleepTime.TotalMilliseconds);
                }
                _clock.UpdateElapsedTime();
                var suppressDraw = true;
                var elapsedAdjustedTime = _clock.ElapsedAdjustedTime;
                if (elapsedAdjustedTime < TimeSpan.Zero)
                {
                    elapsedAdjustedTime = TimeSpan.Zero;
                }
                if (_forceElapsedTimeToZero)
                {
                    elapsedAdjustedTime = TimeSpan.Zero;
                    _forceElapsedTimeToZero = false;
                }
                if (elapsedAdjustedTime > _maximumElapsedTime)
                {
                    elapsedAdjustedTime = _maximumElapsedTime;
                }
                if (_isFixedTimeStep)
                {
                    if (Math.Abs((elapsedAdjustedTime.Ticks - _targetElapsedTime.Ticks)) < (_targetElapsedTime.Ticks >> 6))
                    {
                        elapsedAdjustedTime = _targetElapsedTime;
                    }
                    var accumulatedElapsedGameTime = _accumulatedElapsedGameTime + elapsedAdjustedTime;
                    var targetFrames = accumulatedElapsedGameTime.Ticks / _targetElapsedTime.Ticks;
                    _lastFrameElapsedGameTime = TimeSpan.Zero;
                    if (targetFrames == 0L)
                    {
                        return;
                    }
                    _clock.AdvanceFrameTime();
                    _accumulatedElapsedGameTime = accumulatedElapsedGameTime;
                    var targetElapsedTime = _targetElapsedTime;
                    if (targetFrames > 1L)
                    {
                        _updatesSinceRunningSlowly2 = _updatesSinceRunningSlowly1;
                        _updatesSinceRunningSlowly1 = 0;
                    }
                    else
                    {
                        if (_updatesSinceRunningSlowly1 < int.MaxValue)
                        {
                            _updatesSinceRunningSlowly1++;
                        }
                        if (_updatesSinceRunningSlowly2 < int.MaxValue)
                        {
                            _updatesSinceRunningSlowly2++;
                        }
                    }
                    _drawRunningSlowly = _updatesSinceRunningSlowly2 < 20;
                    while ((targetFrames > 0L) && !_exitRequested)
                    {
                        targetFrames -= 1L;
                        try
                        {
                            _gameTime.ElapsedGameTime = targetElapsedTime;
                            _gameTime.TotalGameTime = _totalGameTime;
                            _gameTime.IsRunningSlowly = _drawRunningSlowly;
                            Update(_gameTime);
                            suppressDraw &= _suppressDraw;
                            _suppressDraw = false;
                        }
                        finally
                        {
                            _accumulatedElapsedGameTime -= targetElapsedTime;
                            _lastFrameElapsedGameTime += targetElapsedTime;
                            _totalGameTime += targetElapsedTime;
                        }
                    }
                }
                else
                {
                    _clock.AdvanceFrameTime();
                    var elapsedGameTime = elapsedAdjustedTime;
                    _drawRunningSlowly = false;
                    _updatesSinceRunningSlowly1 = int.MaxValue;
                    _updatesSinceRunningSlowly2 = int.MaxValue;
                    if (!_exitRequested)
                    {
                        try
                        {
                            _gameTime.ElapsedGameTime = _lastFrameElapsedGameTime = elapsedGameTime;
                            _gameTime.TotalGameTime = _totalGameTime;
                            _gameTime.IsRunningSlowly = false;
                            Update(_gameTime);
                            suppressDraw &= _suppressDraw;
                            _suppressDraw = false;
                        }
                        finally
                        {
                            _totalGameTime += elapsedGameTime;
                        }
                    }
                }
                if (!suppressDraw)
                {
                    _drawingWindow.Invalidate();
                }
            }
        }

        #endregion

        #region Run

        private bool _inRun;
        private bool _endRunRequired;

        /// <summary>
        /// 	Call this method to initialize the game, begin running the game loop, and start processing events for the game.
        /// </summary>
        public void Run()
        {
            try
            {
                ResetStates();
                Initialize();
                _inRun = true;
                BeginRun();
                _gameTime.ElapsedGameTime = TimeSpan.Zero;
                _gameTime.TotalGameTime = _totalGameTime;
                _gameTime.IsRunningSlowly = false;
                Update(_gameTime);
                _doneFirstUpdate = true;
                _startDrawLoop = true;
                _hostTimer.Start();
                _endRunRequired = true;
            }
            catch (Exception ex)
            {
                if (!ShowMissingRequirementMessage(ex))
                {
                    throw;
                }
            }
            finally
            {
                if (!_endRunRequired)
                {
                    _inRun = false;
                }
            }
        }

        /// <summary>
        /// 	Called after all components are initialized but before the first update in the game loop.
        /// </summary>
        protected virtual void BeginRun()
        {
        }

        /// <summary>
        /// 	Called after the game loop has stopped running before exiting.
        /// </summary>
        protected virtual void EndRun()
        {
        }

        #endregion

        #region Exit

        private bool _exitRequested;

        /// <summary>
        /// 	Exits the game.
        /// </summary>
        public void Exit()
        {
            try
            {
                _exitRequested = true;
                _startDrawLoop = false;
                _hostTimer.Stop();
                OnExiting();
                if (_inRun && _endRunRequired)
                {
                    EndRun();
                    _inRun = false;
                }
            }
            catch (Exception ex)
            {
                if (!ShowMissingRequirementMessage(ex))
                {
                    throw;
                }
            }
            finally
            {
                Dispose(true);
            }
        }

        #endregion

        #region Reset

        /// <summary>
        /// 	Resets the elapsed time counter.
        /// </summary>
        public void ResetElapsedTime()
        {
            _forceElapsedTimeToZero = true;
            _drawRunningSlowly = false;
            _updatesSinceRunningSlowly1 = int.MaxValue;
            _updatesSinceRunningSlowly2 = int.MaxValue;
        }

        private void ResetStates()
        {
            _exitRequested = false;
            _doneFirstUpdate = false;
            _startDrawLoop = false;
            _inRun = false;
            _endRunRequired = false;
        }

        #endregion

        #region Error Handling

        /// <summary>
        /// 	This is used to display an error message if there is no suitable graphics device or sound card.
        /// </summary>
        /// <param name="exception"> The exception to display. </param>
        protected virtual bool ShowMissingRequirementMessage(Exception exception)
        {
            MessageBox.Show(exception.Message, Resx.MissingRequirementMessage, MessageBoxButton.OK);
            return true;
        }

        #endregion

        #region Event Handlers

        private void HandleGameComponentsComponentAdded(object sender, GameComponentCollectionEventArgs e)
        {
            if (_inRun)
            {
                e.GameComponent.Initialize();
            }
            else
            {
                _unInitializedGameComponents.Add(e.GameComponent);
            }
            var updateable = e.GameComponent as IUpdateable;
            if (updateable != null)
            {
                var index = _updateableComponents.BinarySearch(updateable, UpdateOrderComparer.Default);
                if (index < 0)
                {
                    index = ~index;
                    while ((index < _updateableComponents.Count) && (_updateableComponents[index].UpdateOrder == updateable.UpdateOrder))
                    {
                        index++;
                    }
                    _updateableComponents.Insert(index, updateable);
                    updateable.UpdateOrderChanged += HandleUpdateableUpdateOrderChanged;
                }
            }
            var drawable = e.GameComponent as IDrawable;
            if (drawable != null)
            {
                var index = _drawableComponents.BinarySearch(drawable, DrawOrderComparer.Default);
                if (index < 0)
                {
                    index = ~index;
                    while ((index < _drawableComponents.Count) && (_drawableComponents[index].DrawOrder == drawable.DrawOrder))
                    {
                        index++;
                    }
                    _drawableComponents.Insert(index, drawable);
                    drawable.DrawOrderChanged += HandleDrawableDrawOrderChanged;
                }
            }
        }

        private void HandleGameComponentsComponentRemoved(object sender, GameComponentCollectionEventArgs e)
        {
            if (!_inRun)
            {
                _unInitializedGameComponents.Remove(e.GameComponent);
            }
            var updateable = e.GameComponent as IUpdateable;
            if (updateable != null)
            {
                _updateableComponents.Remove(updateable);
                updateable.UpdateOrderChanged -= HandleUpdateableUpdateOrderChanged;
            }
            var drawable = e.GameComponent as IDrawable;
            if (drawable != null)
            {
                _drawableComponents.Remove(drawable);
                drawable.DrawOrderChanged -= HandleDrawableDrawOrderChanged;
            }
        }

        private void HandleUpdateableUpdateOrderChanged(object sender, EventArgs e)
        {
            var updateable = sender as IUpdateable;
            if (updateable != null)
            {
                _updateableComponents.Remove(updateable);
                var index = _updateableComponents.BinarySearch(updateable, UpdateOrderComparer.Default);
                if (index < 0)
                {
                    index = ~index;
                    while ((index < _updateableComponents.Count) && (_updateableComponents[index].UpdateOrder == updateable.UpdateOrder))
                    {
                        index++;
                    }
                    _updateableComponents.Insert(index, updateable);
                }
            }
        }

        private void HandleDrawableDrawOrderChanged(object sender, EventArgs e)
        {
            var drawable = sender as IDrawable;
            if (drawable != null)
            {
                _drawableComponents.Remove(drawable);
                var index = _drawableComponents.BinarySearch(drawable, DrawOrderComparer.Default);
                if (index < 0)
                {
                    index = ~index;
                    while ((index < _drawableComponents.Count) && (_drawableComponents[index].DrawOrder == drawable.DrawOrder))
                    {
                        index++;
                    }
                    _drawableComponents.Insert(index, drawable);
                }
            }
        }

        private void HandleGraphicsDeviceManagerRenderModeChanged(object sender, RenderModeChangedEventArgs e)
        {
            if (e.OldRenderMode == RenderMode.Unavailable)
            {
                LoadContent();
            }
            if (e.RenderMode != RenderMode.Hardware)
            {
                switch (GraphicsDeviceManager.Current.RenderModeReason)
                {
                    case RenderModeReason.GPUAccelerationDisabled:
                        ShowMissingRequirementMessage(new InvalidOperationException(Resx.NoGPUAcceleration));
                        break;
                    case RenderModeReason.SecurityBlocked:
                        ShowMissingRequirementMessage(new InvalidOperationException(Resx.HardwareAccelerationBlockedBySecurityReason));
                        break;
                    case RenderModeReason.Not3DCapable:
                        ShowMissingRequirementMessage(new InvalidOperationException(Resx.HardwareAccelerationNotAvailable));
                        break;
                    case RenderModeReason.TemporarilyUnavailable:
                        ShowMissingRequirementMessage(new InvalidOperationException(Resx.HardwareAccelerationNotAvailable));
                        break;
                }
                _drawingWindow.Invalidate();
            }
        }

        #endregion
    }
}